package robocup.component.util;

import java.util.Random;

import robocup.component.PConf;
import robocup.component.SConf;
import robocup.component.geometry.Vektor;
import robocup.component.worldobjects.Ball;
import robocup.component.worldobjects.Player;


/**
 * @author Thomas Karbe
 * 
 * This class contains methods to get random values and random objects.
 *
 */
public class Randoms {
  Random rand;
  final static Randoms instance= new Randoms();
  
  /**
   * Creates a new Randoms object
   */
  public Randoms() {
    rand= new Random(System.currentTimeMillis());
  }
  
  /**
   * This method gives a standard Randoms-object
   * 
   * @return a randoms object
   */
  public static final Randoms getInstance() {
    return instance;
  }

  /**
   * Calculates a Vektor that is on the field
   * 
   * @return a random Vektor on the field
   */
  public final Vektor getRandomVektor() {

    return getRandomVektor(-SConf.getInstance().half_width,
        SConf.getInstance().half_width, -SConf.getInstance().half_length, SConf
            .getInstance().half_length);
  }

  /**
   * Calculates a random Vektor in the given bounds
   * 
   * @param minX the minimum x coordinate (inclusive)
   * @param maxX the maximum x coordinate (exclusive)
   * @param minY the minimum y coordinate (inclusive)
   * @param maxY the maximum y coordinate (exclusive)
   * @return a random Vektor in the given bounds
   */
  public final Vektor getRandomVektor(final double minX,
      final double maxX,
      final double minY,
      final double maxY) {

    double xFact = maxX - minX;
    double yFact = maxY - minY;
    return new Vektor(Vektor.XY, rand.nextDouble() * xFact + minX, rand
        .nextDouble()
        * yFact
        + minY);
  }

  /**
   * Calculates a random double in the given bounds
   * 
   * @param min the minimum bound (inclusive)
   * @param max the maximum bound (exclusive)
   * @return a random double in the given bounds
   */
  public final double getRandomDouble(final double min,
      final double max) {

    double fact = max - min;
    return rand.nextDouble() * fact + min;
  }

  /**
   * Calculates a random player in the same way as the soccer server calculates player types
   *  
   * @return a random player
   */
  public final Player getRandomPlayer() {

    double delta = 0;
    PConf pconf = new PConf();
    pconf.PLAYER_SPEED_MAX = 1.2;
    pconf.STAMINA_INC_MAX = 45;
    delta = rand.nextDouble() * 0.2;
    pconf.PLAYER_DECAY = 0.4 + delta;
    pconf.INERTIA_MOMENT = 5 + delta * 25;
    delta = 0;
    pconf.DASH_POWER_RATE = 0.006;
    pconf.PLAYER_SIZE = 0.3;
    delta = rand.nextDouble() * 0.002;
    pconf.DASH_POWER_RATE += delta;
    pconf.STAMINA_INC_MAX += delta * -10000;
    delta = rand.nextDouble() * 0.2;
    pconf.KICKABLE_MARGIN = 0.7 + delta;
    pconf.KICK_RAND = delta * 0.5;
    delta = rand.nextDouble() * 100;
    pconf.EXTRA_STAMINA = delta;
    pconf.EFFORT_MAX = 1 + delta * -0.002;
    pconf.EFFORT_MIN = 0.6 + delta * -0.002;
    pconf.PLAYER_ACCEL_MAX = 1;
    SConf.getInstance();
    int number = rand.nextInt(10) + 2;
    Vektor pos = new Vektor(Vektor.XY, (rand.nextBoolean() ? rand.nextDouble()
        : -rand.nextDouble())
        * SConf.getInstance().half_width, (rand.nextBoolean() ? rand
        .nextDouble() : -rand.nextDouble())
        * SConf.getInstance().half_length);
    double dir = (rand.nextDouble() * 360.) - 180.;
    double stam = (rand.nextDouble() * (8000. + pconf.EXTRA_STAMINA));
    double rec = rand.nextDouble() * 0.5 + 0.5;
    double neck = (rand.nextDouble() * 180.) - 90.;
    Vektor spd = new Vektor(rand.nextDouble()
        * pconf.PLAYER_SPEED_MAX
        * pconf.PLAYER_DECAY, rand.nextDouble() * 360. - 180.);
    double eff = rand.nextDouble() * 0.4 + pconf.EFFORT_MIN;
    return new Player(0, 0, number, pos, spd, dir, neck, stam, eff, rec, true,
        true, false, pconf);
  }

  /**
   * Calculates a random player in a given area in the same way as the soccer server calculates player types.
   * 
   * @param minPosX the minimum x coordinate (inclusive)
   * @param maxPosX the maximum x coordinate (exclusive)
   * @param minPosY the minimum y coordinate (inclusive)
   * @param maxPosY the maximum y coordinate (exclusive) 
   *  
   * @return a random player
   */
  public final Player getRandomPlayer(final double minPosX, final double maxPosX, final double minPosY, final double maxPosY) {

    double delta = 0;
    PConf pconf = new PConf();
    pconf.PLAYER_SPEED_MAX = 1.2;
    pconf.STAMINA_INC_MAX = 45;
    delta = rand.nextDouble() * 0.2;
    pconf.PLAYER_DECAY = 0.4 + delta;
    pconf.INERTIA_MOMENT = 5 + delta * 25;
    delta = 0;
    pconf.DASH_POWER_RATE = 0.006;
    pconf.PLAYER_SIZE = 0.3;
    delta = rand.nextDouble() * 0.002;
    pconf.DASH_POWER_RATE += delta;
    pconf.STAMINA_INC_MAX += delta * -10000;
    delta = rand.nextDouble() * 0.2;
    pconf.KICKABLE_MARGIN = 0.7 + delta;
    pconf.KICK_RAND = delta * 0.5;
    delta = rand.nextDouble() * 100;
    pconf.EXTRA_STAMINA = delta;
    pconf.EFFORT_MAX = 1 + delta * -0.002;
    pconf.EFFORT_MIN = 0.6 + delta * -0.002;
    pconf.PLAYER_ACCEL_MAX = 1;
    SConf.getInstance();
    int number = rand.nextInt(10) + 2;
    Vektor pos = getRandomVektor(minPosX, maxPosX, minPosY, maxPosY);
    double dir = (rand.nextDouble() * 360.) - 180.;
    double stam = (rand.nextDouble() * (8000. + pconf.EXTRA_STAMINA));
    double rec = rand.nextDouble() * 0.5 + 0.5;
    double neck = (rand.nextDouble() * 180.) - 90.;
    Vektor spd = new Vektor(rand.nextDouble()
        * pconf.PLAYER_SPEED_MAX
        * pconf.PLAYER_DECAY, rand.nextDouble() * 360. - 180.);
    double eff = rand.nextDouble() * 0.4 + pconf.EFFORT_MIN;
    return new Player(0, 0, number, pos, spd, dir, neck, stam, eff, rec, true,
        true, false, pconf);
  }

  /**
   * Calculates a random goalie
   *  
   * @return a random goalie
   */
  public final Player getRandomGoalie() {

    PConf pconf = new PConf();
    int number = 1;
    Vektor pos = new Vektor(Vektor.XY, (rand.nextBoolean() ? rand.nextDouble()
        : -rand.nextDouble())
        * SConf.getInstance().half_width, (rand.nextBoolean() ? rand
        .nextDouble() : -rand.nextDouble())
        * SConf.getInstance().half_length);
    double dir = (rand.nextDouble() * 360.) - 180.;
    double stam = (rand.nextDouble() * (8000. + pconf.EXTRA_STAMINA));
    double rec = rand.nextDouble() * 0.5 + 0.5;
    double neck = (rand.nextDouble() * 180.) - 90.;
    Vektor spd = new Vektor(rand.nextDouble()
        * pconf.PLAYER_SPEED_MAX
        * pconf.PLAYER_DECAY, rand.nextDouble() * 360. - 180.);
    double eff = rand.nextDouble() * 0.4 + pconf.EFFORT_MIN;
    return new Player(0, 0, number, pos, spd, dir, neck, stam, eff, rec, true,
        true, false, pconf);
  }

  /**
   * Calculates a random goalie in a given area.
   * 
   * @param minPosX the minimum x coordinate (inclusive)
   * @param maxPosX the maximum x coordinate (exclusive)
   * @param minPosY the minimum y coordinate (inclusive)
   * @param maxPosY the maximum y coordinate (exclusive) 
   *  
   * @return a random goalie
   */
  public final Player getRandomGoalie(final double minPosX, final double maxPosX, final double minPosY, final double maxPosY) {

    PConf pconf = new PConf();
    SConf.getInstance();
    int number = 1;
    Vektor pos = getRandomVektor(minPosX, maxPosX, minPosY, maxPosY);
    double dir = (rand.nextDouble() * 360.) - 180.;
    double stam = (rand.nextDouble() * (8000. + pconf.EXTRA_STAMINA));
    double rec = rand.nextDouble() * 0.5 + 0.5;
    double neck = (rand.nextDouble() * 180.) - 90.;
    Vektor spd = new Vektor(rand.nextDouble()
        * pconf.PLAYER_SPEED_MAX
        * pconf.PLAYER_DECAY, rand.nextDouble() * 360. - 180.);
    double eff = rand.nextDouble() * 0.4 + pconf.EFFORT_MIN;
    return new Player(0, 0, number, pos, spd, dir, neck, stam, eff, rec, true,
        true, true, pconf);
  }
  
  /**
   * Calculates a random ball on the field.
   * 
   * @return a random ball.
   */
  public final Ball getRandomBall() {
    Vektor pos = new Vektor(Vektor.XY, (rand.nextBoolean() ? rand.nextDouble()
        : -rand.nextDouble())
        * SConf.getInstance().half_width, (rand.nextBoolean() ? rand
        .nextDouble() : -rand.nextDouble())
        * SConf.getInstance().half_length);
    Vektor spd = new Vektor(rand.nextDouble()
        * SConf.getInstance().BALL_SPEED_MAX
        * SConf.getInstance().BALL_DECAY, rand.nextDouble() * 360. - 180.);
    return new Ball(0,0,pos, spd);
  }
  
  /**
   * Calculates a random ball in the given area.
   *  
   * @param minPosX the minimum x coordinate (inclusive)
   * @param maxPosX the maximum x coordinate (exclusive)
   * @param minPosY the minimum y coordinate (inclusive)
   * @param maxPosY the maximum y coordinate (exclusive)
   * 
   * @return a random ball.
   */
  public final Ball getRandomBall(final double minPosX, final double maxPosX, final double minPosY, final double maxPosY) {
    Vektor pos = getRandomVektor(minPosX, maxPosX, minPosY, maxPosY);
    Vektor spd = new Vektor(rand.nextDouble()
        * SConf.getInstance().BALL_SPEED_MAX
        * SConf.getInstance().BALL_DECAY, rand.nextDouble() * 360. - 180.);
    return new Ball(0,0,pos, spd);
  }
  
  /**
   * Calculates a random ball in the kickable margin of the given player.
   * 
   * @param p the player that can kick the ball
   * @return a ball that is kickable by the given player
   */
  public final Ball getRandomBallInKickable(final Player p) {
    Vektor spd= new Vektor(rand.nextDouble()*SConf.getInstance().BALL_SPEED_MAX, rand.nextDouble()*360.-180.);
    double possDist= p.getPConf().KICKABLE_MARGIN;
    double size= p.getPConf().PLAYER_SIZE+SConf.getInstance().BALL_SIZE;
    Vektor posi= new Vektor(rand.nextDouble()*possDist+size, rand.nextDouble()*360.-180.);
    posi.addToThis(p.getPosition());
    return new Ball(0,0,posi, spd);
  }
  
  /**
   * Calculates a random ball in the tacklable area of the given player.
   * 
   * @param p the player that can tackle the ball
   * @return a ball that is tackleable by the given player
   */
  public final Ball getRandomBallInTackleable(Player p) {
    Vektor spd= new Vektor(rand.nextDouble()*SConf.getInstance().BALL_SPEED_MAX, rand.nextDouble()*360.-180.);
    double possDist= SConf.getInstance().TACKLE_DIST - p.getPConf().PLAYER_SIZE;
    double sideDist= SConf.getInstance().TACKLE_WIDTH;
    possDist *= rand.nextDouble();
    sideDist *= rand.nextDouble();
    if (rand.nextBoolean()) {
      sideDist= -sideDist;
    }
    Vektor tackVek= new Vektor(Vektor.XY, sideDist, possDist);
    double angle= tackVek.getAngle();
    double dist= tackVek.getLength();
    angle+= p.getBodyDir();
    tackVek.pointAtPolar(dist, angle);
    tackVek.addToThis(p.getPosition());
    return new Ball(0,0,tackVek, spd);
  }
  
  /**
   * Returns a random boolean
   * @return a random boolean
   */
  public final boolean getRandomBoolean() {
    return rand.nextBoolean();
  }
  
  /**
   * Returns a random int int the given range
   * @param min the minimum border (inclusive)
   * @param max the maximum border (inclusive)
   * @return a random int
   */
  public final int getRandomInt(int min, int max) {
    return rand.nextInt(max-min+1)+min;
  }

}
